package com.example.springboot.interceptor;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.HashMap;
import java.util.Objects;
import java.util.concurrent.TimeUnit;

/**
 * <p>自定义拦截器，对同一个请求进行限流，只允许5秒中访问一次
 * <p>
 * @author Zhang.Z
 * @version 2025-09-20
 */
public class RepeatSubmitInterceptor implements HandlerInterceptor {

    private static final Logger logger = LoggerFactory.getLogger(RepeatSubmitInterceptor.class);

    private final HashMap<String, Long> urlMap = new HashMap<>();

    private static final Long NANO = TimeUnit.NANOSECONDS.convert(5, TimeUnit.SECONDS);


    /**
     * preHandler方法，在Controller方法之前执行
     * <p> 此处只拦截标注了@RepeatSubmit的
     * <p>判断条件仅仅是用了uri，实际开发中根据实际情况组合一个唯一识别的条件。
     * <p> 比如IP地址，用户唯一标识、请求参数、请求URI等等其中的某一个或者多个的组合
     */
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        if (handler instanceof HandlerMethod) {

            HandlerMethod method = (HandlerMethod) handler;
            //标注在方法上的@RepeatSubmit
            RepeatSubmit repeatSubmitByMethod = AnnotationUtils.findAnnotation(method.getMethod(), RepeatSubmit.class);
            //标注在controler类上的@RepeatSubmit
            RepeatSubmit repeatSubmitByCls = AnnotationUtils.findAnnotation(method.getMethod().getDeclaringClass(), RepeatSubmit.class);
            //没有限制重复提交，直接跳过
            if (Objects.isNull(repeatSubmitByMethod) && Objects.isNull(repeatSubmitByCls))
                return true;

            // todo: 组合判断条件，这里仅仅是演示，实际项目中根据架构组合条件
            //请求的URI
            String uri = request.getRequestURI();

            if (checkWhite(uri)) return true;


            //存在并在时间范围内即返回true，不存在或已超时即返回false
            Boolean exist = checkExist(uri);

            //如果存在，表示5s内请求过，直接抛出异常，由全局异常进行处理返回指定信息
            if (exist) {
                returnJson(response, "请求次数频繁，请稍后");
                return false;
            }


        }
        return true;
    }

    /**
     * 白名单
     */
    private Boolean checkWhite(String uri) {
        if ("/user/register".equals(uri)) {
            return true;
        }
        return false;
    }

    /**
     * 判断是否存在或过期
     */
    private Boolean checkExist(String uri) {
        long timeMillis = System.nanoTime();
        Long exist = urlMap.getOrDefault(uri, -1L);
        if (exist == -1) {
            urlMap.put(uri, timeMillis);
            return false;
        }
        long difference = timeMillis - exist;

        if (difference >= NANO) {
            urlMap.put(uri, timeMillis);
            return false;
        }

        return true;
    }


    private void returnJson(HttpServletResponse response, String msg) {
        response.setCharacterEncoding("UTF-8");
        response.setContentType("text/html; charset=utf-8");

        try (PrintWriter writer = response.getWriter()) {
            writer.print(msg);
            writer.flush();
        } catch (IOException e) {
            logger.error("response error", e);
        }
    }
}